//********************************************************************************
//
// File :           ph02_gen_random_data.cpp
//
// Description :    Generate Random Data For Sorting Tests.
//
// Dependencies:
//
// Author :         UlMonkey1987( WU Kan )
//
// Stu NO. :        2010212404
//
// Date :           2011-4-17
//
//********************************************************************************

#include <iostream>
#include <fstream>

#include <cstdlib>
#include <ctime>
#include <cmath>
#include <algorithm>

#include <string>

using namespace std;

// bool genRandomNums( long* pNum, const unsigned long numN, const long numMin, const long numMax )
bool genNormalRandomNums( long* pNum, const unsigned long numN, const long numMin, const long numMax )
{
    if ( numMin >= numMax ) return false;

    srand( ( unsigned ) time( NULL ) );

    // generate
    for ( unsigned long i = 0; i < numN; i ++ )
    {
        pNum[i] = numMin + ( long )( ( rand() * 1.0 / RAND_MAX ) * ( numMax - numMin ) );
    }
    // ! generate

    // check
    for ( unsigned long i = 0; i < numN; i ++ )
    {
        if ( pNum[i] < numMin || pNum[i] > numMax )
        {
            delete[] pNum; pNum = 0; return false;
        }
    }
    // ! check

    return true;
}
//

// bool genNearlyOrderedRandomNums( long* pNum, const unsigned long numN, const long numMin, const long numMax )
bool genNearlyOrderedRandomNums( long* pNum, const unsigned long numN, const long numMin, const long numMax )
{
    if ( numMin >= numMax ) return false;

    srand( ( unsigned ) time( NULL ) );

    unsigned long subArraySize = 1024;
    unsigned long subArrayN = ( numN + subArraySize - 1 ) / subArraySize;
    unsigned long subArrayLastSize = ( 0 == numN % subArraySize ? subArraySize : numN % subArraySize );

    // set sub arrays and gen test data
    long** pSubNum = new long*[subArrayN];
    unsigned long numIndex = 0;
    for ( unsigned long i = 0; i < subArrayN - 1; i ++ )
    {
        pSubNum[i] = new long[subArraySize];
        for ( unsigned long j = 0; j < subArraySize; j ++ )
            pSubNum[i][j] = numMin + ( long )( ( rand() * 1.0 / RAND_MAX ) * ( numMax - numMin ) );

        sort( pSubNum[i], pSubNum[i] + subArraySize );

        for ( unsigned long j = 0; j < subArraySize; j ++ )
        {
            pNum[numIndex] = pSubNum[i][j]; numIndex ++;
            if ( numIndex >= numN ) break;
        }

        delete []pSubNum[i]; pSubNum[i] = 0;
    }
    pSubNum[subArrayN-1] = new long[subArrayLastSize];
    for ( unsigned long j = 0; j < subArrayLastSize; j ++ )
        pSubNum[subArrayN-1][j] = numMin + ( long )( ( rand() * 1.0 / RAND_MAX ) * ( numMax - numMin ) );

    sort( pSubNum[subArrayN-1], pSubNum[subArrayN-1] + subArrayLastSize );

    for ( unsigned long j = 0; j < subArrayLastSize; j ++ )
    {
        pNum[numIndex] = pSubNum[subArrayN-1][j]; numIndex ++;
        if ( numIndex >= numN ) break;
    }
    delete []pSubNum[subArrayN-1]; pSubNum[subArrayN-1] = 0;

    // ! set sub arrays and gen test data

    delete []pSubNum; pSubNum = 0;

    // check result
    for ( unsigned long i = 0; i < numN; i ++ )
    {
        if ( pNum[i] < numMin || pNum[i] > numMax )
        {
            delete[] pNum; pNum = 0; return false;
        }
    }
    // ! check result

    return true;
}
//

// bool genNearlyDisorderedRandomNums( long* pNum, const unsigned long numN, const long numMin, const long numMax )
bool genNearlyDisorderedRandomNums( long* pNum, const unsigned long numN, const long numMin, const long numMax )
{
    if ( numMin >= numMax ) return false;

    srand( ( unsigned ) time( NULL ) );

    unsigned long subArraySize = 1024;
    unsigned long subArrayN = ( numN + subArraySize - 1 ) / subArraySize;
    unsigned long subArrayLastSize = ( 0 == numN % subArraySize ? subArraySize : numN % subArraySize );

    // set sub arrays and gen test data
    long** pSubNum = new long*[subArrayN];
    unsigned long numIndex = 0;
    for ( unsigned long i = 0; i < subArrayN - 1; i ++ )
    {
        pSubNum[i] = new long[subArraySize];
        for ( unsigned long j = 0; j < subArraySize; j ++ )
            pSubNum[i][j] = numMin + ( long )( ( rand() * 1.0 / RAND_MAX ) * ( numMax - numMin ) );

        sort( pSubNum[i], pSubNum[i] + subArraySize );
        reverse( pSubNum[i], pSubNum[i] + subArraySize );

        for ( unsigned long j = 0; j < subArraySize; j ++ )
        {
            pNum[numIndex] = pSubNum[i][j]; numIndex ++;
            if ( numIndex >= numN ) break;
        }

        delete []pSubNum[i]; pSubNum[i] = 0;
    }
    pSubNum[subArrayN-1] = new long[subArrayLastSize];
    for ( unsigned long j = 0; j < subArrayLastSize; j ++ )
        pSubNum[subArrayN-1][j] = numMin + ( long )( ( rand() * 1.0 / RAND_MAX ) * ( numMax - numMin ) );

    sort( pSubNum[subArrayN-1], pSubNum[subArrayN-1] + subArrayLastSize );
    reverse( pSubNum[subArrayN-1], pSubNum[subArrayN-1] + subArrayLastSize );

    for ( unsigned long j = 0; j < subArrayLastSize; j ++ )
    {
        pNum[numIndex] = pSubNum[subArrayN-1][j]; numIndex ++;
        if ( numIndex >= numN ) break;
    }
    delete []pSubNum[subArrayN-1]; pSubNum[subArrayN-1] = 0;

    // ! set sub arrays and gen test data

    delete []pSubNum; pSubNum = 0;

    // check result
    for ( unsigned long i = 0; i < numN; i ++ )
    {
        if ( pNum[i] < numMin || pNum[i] > numMax )
        {
            delete[] pNum; pNum = 0; return false;
        }
    }
    // ! check result

    return true;
}
//

// bool writeData( const string& strFileName, const long* pNum, const unsigned long numN )
bool writeData( const string& strFileName, const long* pNum, const unsigned long numN )
{
    char* pByteNum = ( char* )pNum;
    ofstream fout( strFileName.c_str(), iostream::binary );
    fout.write( pByteNum, numN * sizeof( long ) );

    return true;
}
//

// bool genRandNums( const string& strNormalRandNumFileName,
//                   const string& strNearlyOrderedRandNumFileName,
//                   const string& strNearlyDisorderedRandNumFileName )
//                   const unsigned long numN, const long numMin, const long numMax )
bool genRandNums( const string& strNormalRandNumFileName,
                  const string& strNearlyOrderedRandNumFileName,
                  const string& strNearlyDisorderedRandNumFileName,
                  const unsigned long numN, const long numMin, const long numMax )
{
    long *pNum = new long[numN];

    bool isProcOK = false;

    isProcOK = genNormalRandomNums( pNum, numN, numMin, numMax );
    isProcOK = writeData( strNormalRandNumFileName, pNum, numN );

    isProcOK = genNearlyOrderedRandomNums( pNum, numN, numMin, numMax );
    isProcOK = writeData( strNearlyOrderedRandNumFileName, pNum, numN );

    isProcOK = genNearlyDisorderedRandomNums( pNum, numN, numMin, numMax );
    isProcOK = writeData( strNearlyDisorderedRandNumFileName, pNum, numN );

    delete[] pNum; pNum = 0;

    return isProcOK;
}
//

int main( int argc, char** argv )
{
    if ( argc < 7 )
    {
        cout << "error : lack args." << endl;
        cout << "usage : ";
        cout << "<program_name> ";
        cout << "<rand_num_count> ";
        cout << "<rand_num_min> ";
        cout << "<rand_num_max> ";
        cout << "<normal_rand_data_file_path> ";
        cout << "<nearly_ordered_rand_data_file_path> ";
        cout << "<nearly_disordered_rand_data_file_path>" << endl;

        return 1;
    }

    unsigned long numN = atoi( argv[1] );
    long numMin = atoi( argv[2] );
    long numMax = atoi( argv[3] );
    string strNormalRandNumFileName( argv[4] );
    string strNearlyOrderedRandNumFileName( argv[5] );
    string strNearlyDisorderedRandNumFileName( argv[6] );

    if ( numN < 1024 || numN > 200000000 )
    {
        cout << "error : numN must be in range [1024, 200000000]." << endl;

        return 1;
    }
    else if ( strNormalRandNumFileName.empty()
                || strNearlyOrderedRandNumFileName.empty()
                || strNearlyDisorderedRandNumFileName.empty() )
    {
        cout << "error : the output file name should not be null." << endl;

        return 1;
    }

//     // for test
//      unsigned long numN = 10000;
//      long numMin = 0;
//      long numMax = 1000;
//      string strNormalRandNumFileName = "1.raw";
//      string strNearlyOrderedRandNumFileName = "2.raw";
//      string strNearlyDisorderedRandNumFileName = "3.raw";
//     // ! for test

    bool isProcOK = genRandNums( strNormalRandNumFileName,
                                 strNearlyOrderedRandNumFileName,
                                 strNearlyDisorderedRandNumFileName,
                                 numN, numMin, numMax );

    if ( ! isProcOK )
    {
        cout << endl;
        cout << "Data Generation Failed." << endl << endl;

        return 1;
    }

    cout << endl;
    cout << "Data Generation Successful. Please Refer To : ";
    cout << strNormalRandNumFileName << ", ";
    cout << strNearlyOrderedRandNumFileName << ", ";
    cout << strNearlyDisorderedRandNumFileName << " For Details." << endl << endl;

    //system( "pause" );

    return 0;
}
